<?php
/**
 * Zead
 *
 * LICENSE	
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.opensource.org/licenses/bsd-license.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to eu@marcelomx.com so we can send you a copy immediately.
 *
 * @category   Zead
 * @author     Marcelo Rodrigues Gonzaga <eu@marcelomx.com>
 * @copyright  Copyright (c) 2008 Marcelo Rodrigues Gonzaga
 * @link       http://code.google.com/p/zead 
 * @license    http://www.opensource.org/licenses/bsd-license.php     New BSD License
 *
 **/


/** @see Zead_Loader **/
require_once 'Zead/Loader.php'; 

/** @see Zead_Player_Exception **/
require_once 'Zead/Player/Exception.php';

/** @see Zead_Player_Request */
require_once 'Zead/Player/Request.php';

/** @see Zead_Player_Action */
require_once 'Zead/Player/Action.php';

/**
 * Class Zead_Player 
 * 
 * @category   Zead
 * @package    Zead_Player
 * @copyright  Copyright (c) 2008 Marcelo Rodrigues Gonzaga 
 * @license    http://www.opensource.org/licenses/bsd-license.php     New BSD License  
 */
class Zead_Player 
{
	/**
	 * A instância única do controlador
	 *  
	 * @var Zead_Player 
	 */
	protected static $_instance = null;

	/** 
	 * Os parametros do controlador
	 * @var array
	 */
	protected $_params  = array();

	/**
 	 * Plugins para serem aplicados antes e depois do processamento
	 * @var array
	 */
	protected $_plugins = array();
	
	/**
	 * Registro da hora inicial
	 * 
	 * @var int
	 */
	protected $_startTime = 0;	

	/** 
	 * O objeto Request 
	 * 
	 * @var Zead_Player_Request 
	 */
	protected $_request = null;
	
	/** 
	 * O objeto Response
	 * 
	 * @var Zead_Player_Action 
	 */
	protected $_response = null;	

	/**
	 * Class constructor
	 */
	protected function __construct() 
	{		
		// Setando o diretório de cache
		$this->setParam('useCache', false);
		$this->setParam('cacheDir', $_ENV['TMP']);
	}

	/**
	 * Metodo magico para recuperar o valor de um parametro do controlador
	 * @param string $key O nome do parametro
	 * @return mixed
	 */
	public function __get($key)
	{
		return $this->getParam($key);
	}

	/**
	 * Metodo magico para atribuir valores aos parametros do controlador
	 * @param string $key
	 * @param mixed  $value
	 */
	public function __set($key, $value)
	{
		$this->setParam($key, $value);
	}
	
	/**
	 * Retorna a isntancia UNICA do Controlador
	 * @return Zead_Player
	 */
	public static function getInstance() 
	{
		if (null === self::$_instance) {
			self::$_instance = new self();
		}
		return self::$_instance;
	}

	/**
	 * Recupera a instancia do controlador a despacha automaticamente.
	 * @return void
	 */
	public static function run() 
	{
		self::getInstance()->dispatch();
	}
	
	/**
	 * Retorna a lista de parametros registrados para o frontController
	 * 
	 * @return array
	 */
	public function getParams()
	{
		$params = array();
		
		foreach ($this->_params as $key => $value) {
			if (is_object($value)) {
				$params[$key] = 'Object ' . get_class($value) . '{}';
			} else {
				$params[$key] = $value;
			}
		}
		return $params;
	}

	/**
 	 * Recupera um parametro do controlador
	 * @param  string $key O nome do parametro
	 * @return mixed  O valor do parametro registrado
	 */
	public function getParam($key) 
	{
		return $this->_params[(string) $key];
	}	

	/**
	 * Seta um valor para um parametro, caso o mesmo mesmo não exista
	 * será criado, caso o valor seja nulo, será excluído.
	 * 
	 * @param  string $key   O nome do parametro
	 * @param  mixed  $value O valor do parametro
	 * @return Zead_Player O Controlador
	 */
	public function setParam($key, $value = null) 
	{
		$key = (string) $key;
		
		if (null == $value && isset($this->_params[$key]))
			unset($this->_params[$key]);
		else
			$this->_params[$key] = $value;
			
		return $this;
	}

	/**
	 * Registra um plugin. O objeto deve ser uma instancia da classe
	 * Zead_Player_Plugin_Abstract
	 * 
	 * @param  Zead_Player_Plugin_Abstract $plugin
	 * @return Zead_Player
	 */
	public function registerPlugin(Zead_Player_Plugin_Abstract $plugin)
	{
		$this->_plugins[$plugin->getName()] = $plugin;
		return $this;
	}

	/**
	 * Recupera um plugin pelo nome. Do contrário, gera uma exceção.
	 * 

	 * @param  string $name  O nome do plugin a ser recuperado
	 * @return Zead_Player_Plugin_Abstract
	 */
	public function getPlugin($name)
	{
		if (!isset($this->_plugins[$name]))
			throw new Zead_Player_Exception('The plugin '. $name . ' is not registered');
		
		return $this->_plugins[$name];
	}

	/**
	 * Seta o objeto de REQUEST
	 * 
	 * @param  Zend_Controller_Request_Abstract $request O objeto REQUEST
	 * @return Zead_Player
	 */
	public function setRequest($request) 
	{
		if (is_string($request)) {
			Zead_Loader::loadClass($request);
			$request = new $request();
		}
		if (!$request instanceof Zend_Controller_Request_Abstract) {
			throw new Zead_Player_Exception('Invalid request class');
		}
		
		$this->_request = $request;

		return $this;
	}

	/**
	 * Retorna o objeto REQUEST
	 * @return Zend_Controller_Request_Abstract
	 */
	public function getRequest() 
	{
		return $this->_request;
	}

	/**
	 * Seta o objeto RESPONSE (resposta)
	 * 
	 * @param  Zend_Controller_Response_Abstract $response
	 * @return Zead_Player
	 */
	public function setResponse($response) 
	{
		if (is_string($response)) {
			Zead_Loader::loadClass($response);
			$response = new $response();
		}
		if (!$response instanceof Zend_Controller_Response_Abstract) {
			throw new Zead_Player_Exception('Invalid response class');
		}
		
		$this->_response = $response;

		return $this;
	}

	/**
	 * Retorna o objeto RESPONSE
	 * @return Zend_Controller_Response_Abstract
	 */	
	public function getResponse() 
	{
		return $this->_response;
	}

	/**
	 * Executa a instancia da aplicacao.
	 *
	 * @param Zend_Controller_Request_Abstract  $request  O objeto request  (requisicao)
	 * @param Zend_Controller_Response_Abstract $response O objeto response (resposta)
	 */
	public function dispatch(Zend_Controller_Request_Abstract $request = null, 
		Zend_Controller_Response_Abstract $response = null) {


		// Set request
		if (null !== $request) {
			$this->setRequest($request);
		} else if ((null === $request) && (null === ($request = $this->getRequest()))) {
			Zead_Loader::loadClass('Zead_Player_Request');
			$request = new Zead_Player_Request();
			$this->setRequest($request);
		}

		// Set response
		if (null !== $response) {
			$this->setResponse($response);
		} else if ((null === $response) && (null === ($response = $this->getResponse()))) {
			Zead_Loader::loadClass('Zead_Player_Response');
			$response = new Zead_Player_Response();
			$this->setResponse($response);
		}

		// Register the initial plugins
		foreach (array('Auth', 'Manifest') as $pluginName) {
			$className = 'Zead_Player_Plugin_'. $pluginName;
			Zend_Loader::loadClass($className);
			$plugin = new $className();
			$plugin->setRequest($request);
			$plugin->setResponse($response);
			$this->registerPlugin($plugin);
		}

		// Pre dispatch the plugins
		foreach ($this->_plugins as $plugin) {
			$plugin->preDispatch();
		}
		
		// Dispatch the resource (action)		
		$className   = 'Zead_Player_Action_'. ucfirst($request->getAction());
		Zend_Loader::loadClass($className);		
		$object = new $className($request, $response);
		$object->init();
		$object->dispatch();

		// Post dispatch the plugins
		foreach ($this->_plugins as $plugin) {
			$plugin->postDispatch();
		}

		// Enviando a resposta para o browser (cabecalhos e o conteudo do body)
		$response->sendResponse();

		// Exit the app front
		exit();
	}
}
